using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using T.FAS.Runtime.Action.Common;
using T.FAS.Runtime.Action.ExpressionPlugin;
using T.FAS.Runtime.Metadata.Entity;
using T.FAS.Runtime.Action.SPI;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.DataAccess.Service;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.FAS.Runtime.Expression.Entity;
using T.FAS.Runtime.Metadata.Client;
//using T.FAS.Runtime.Metadata.SPI;
using static T.FAS.Runtime.Metadata.Entity.DataModel.DataModelErrorCode;
using DataType = T.FAS.Runtime.Base.Entity.DataType;

namespace T.FAS.Runtime.Action.Service
{
    public class ActionRTPService
    {
        public List<ActionInfoWithSchema> GetActionInfoWithSchema(List<string> actionIds)
        {
            var result = new List<ActionInfoWithSchema>();
            actionIds.ForEach(id =>
            {
                var info = new ActionInfoWithSchema();
                var action = MetadataRTPService.GetInstance().GetMetadata(id, SPI.ActionType.Action);
                if (action != null)
                {
                    var actionMetaData = ActionRTPUtil.ActionMetadataValid(action.MetaDataContent);
                    var schema = GetActionTSchema(actionMetaData);
                    actionMetaData.ActionSchemaRefresh(schema);
                    var actionResultSchema = BuildEmptyTResultFromTSchema(actionMetaData, SPI.ActionType.Action);
                    info.ActionID = id;
                    info.ActionMetaData = actionMetaData;
                    info.ActionSchema = schema;
                    info.ActionTResultSchema = actionResultSchema;
                    result.Add(info);
                }
            });
            return result;
        }
        public List<ActionInfoWithSchema> GetActionInfoWithSchema(Dictionary<string, List<string>> dic)
        {
            var result = new List<ActionInfoWithSchema>();
            var actionIds = new List<string>();
            var dqIds = new List<string>();
            var cdoIds = new List<string>();
            var eoIds = new List<string>();
            var doIds = new List<string>();
            var cdoCodes = new List<string>();
            dic.TryGetValue("action", out actionIds);
            dic.TryGetValue("dataquery", out dqIds);
            dic.TryGetValue("cdo", out cdoIds);
            dic.TryGetValue("eo", out eoIds);
            dic.TryGetValue("do", out doIds);
            dic.TryGetValue("cdo_Code", out cdoCodes);
            if (actionIds != null && actionIds.Count > 0)
            {
                var actionSchemas = GetInfoWithSchemaForAction(actionIds);
                result.AddRange(actionSchemas);
            }
            if (dqIds != null && dqIds.Count > 0)
            {
                var dqSchemas = GetInfoWithSchemaForDQ(dqIds);
                result.AddRange(dqSchemas);
            }
            if (cdoIds != null && cdoIds.Count > 0)
            {
                var cdoSchemas = GetInfoWithSchemaForCDO(cdoIds);
                result.AddRange(cdoSchemas);
            }
            if (cdoCodes != null && cdoCodes.Count > 0)
            {
                var cdoSchemas = GetInfoWithSchemaForCDOByCode(cdoCodes);
                result.AddRange(cdoSchemas);
            }
            if (eoIds != null && eoIds.Count > 0)
            {
                var eoSchemas = GetInfoWithSchemaForEO(eoIds);
                result.AddRange(eoSchemas);
            }
            if (doIds != null && doIds.Count > 0)
            {
                var doSchemas = GetInfoWithSchemaForDO(doIds);
                result.AddRange(doSchemas);
            }
            return result;
        }

        private List<ActionInfoWithSchema> GetInfoWithSchemaForAction(List<string> actionIds)
        {
            var result = new List<ActionInfoWithSchema>();
            foreach (var id in actionIds)
            {
                if (string.IsNullOrEmpty(id))
                {
                    throw new Exception($"please check your Page ViewModel, there must be some Actions ID is null or empty");
                }
                var action = MetadataRTPService.GetInstance().GetMetadata(id, SPI.ActionType.Action);
                if (action != null)
                {
                    var cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(action.MetaDataContent);
                    if (cachedInfo != null && cachedInfo.ActionTResultSchema != null)
                    {
                        result.Add(cachedInfo);
                    }
                    else
                    {
                        var info = new ActionInfoWithSchema();
                        var actionMetaData = ActionRTPUtil.ActionMetadataValid(action.MetaDataContent);
                        var schema = GetActionTSchema(actionMetaData);
                        actionMetaData.ActionSchemaRefresh(schema);
                        var actionResultSchema = BuildEmptyTResultFromTSchema(actionMetaData, SPI.ActionType.Action);
                        info.ActionID = id;
                        info.ActionCode = actionMetaData.Code;
                        info.ActionMetaData = actionMetaData;
                        info.ActionSchema = schema;
                        info.ActionTResultSchema = actionResultSchema;
                        info.ActionType = SPI.ActionType.Action;
                        info.LastModifyTime = action.MetaDataContent.LastModifyTime;
                        result.Add(info);
                        cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(action.MetaDataContent);
                        if (cachedInfo == null)
                        {
                            ActionRTPLocalCache.GetInstance().AddCache(action.MetaDataContent, info);
                        }
                    }
                }
            }

            return result;
        }
        private List<ActionInfoWithSchema> GetInfoWithSchemaForDQ(List<string> dqIds)
        {
            var result = new List<ActionInfoWithSchema>();
            dqIds.ForEach(id =>
            {
                var dataQuery = MetadataRTPService.GetInstance().GetMetadata(id, SPI.ActionType.DQ);
                if (dataQuery != null)
                {
                    var cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(dataQuery.MetaDataContent);
                    if (cachedInfo != null && cachedInfo.ActionTResultSchema != null)
                    {
                        result.Add(cachedInfo);
                    }
                    else
                    {
                        var info = new ActionInfoWithSchema();
                        var DQ = dataQuery.MetaDataContent as DataQuery;
                        if (DQ == null)
                            throw new Exception(string.Format(CommonStringClassService.ActionRTPService_GetInfoWithSchemaForDQ, id));
                        var schema = DQ.BuildSchema();
                        schema.ActionID = DQ.ID;
                        schema.ActionCode = DQ.Code;
                        schema.ActionName = DQ.Name;
                        schema.ResultType = DataType.List;
                        var dqResultSchema = BuildEmptyTResultFromTSchema(DQ, SPI.ActionType.DQ);
                        info.ActionID = id;
                        info.ActionSchema = schema;
                        info.ActionTResultSchema = dqResultSchema;
                        info.ActionType = SPI.ActionType.DQ;
                        info.LastModifyTime = dataQuery.MetaDataContent.LastModifyTime;
                        //info.ActionMetaData = DQ;
                        info.ActionMetaData = new ActionMetaData()
                        {
                            ID = DQ.ID,
                            Code = DQ.Code,
                            Name = DQ.Name,
                        };
                        result.Add(info);
                        cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(dataQuery.MetaDataContent);
                        if (cachedInfo == null)
                        {
                            ActionRTPLocalCache.GetInstance().AddCache(dataQuery.MetaDataContent, info);
                        }
                    }
                }
            });

            return result;
        }
        private List<ActionInfoWithSchema> GetInfoWithSchemaForCDOByCode(List<string> codCodes)
        {
            var result = new List<ActionInfoWithSchema>();
            codCodes.ForEach(code =>
            {
                var cdo = MetadataRTPService.GetInstance().GetMetadataByCode(code, ActionRTPConst.MetaDataType_CDO);
                CreateCDOActionInfo(code, cdo, result);
            });
            return result;
        }
        private List<ActionInfoWithSchema> GetInfoWithSchemaForCDO(List<string> cdoList)
        {
            var result = new List<ActionInfoWithSchema>();
            cdoList.ForEach(id =>
            {
                var cdo = MetadataRTPService.GetInstance().GetMetadata(id, SPI.ActionType.CDO).MetaDataContent;
                CreateCDOActionInfo(id, cdo, result); ;
            });
            return result;
        }

        private void CreateCDOActionInfo(string id, IMetaDataContent cdo, List<ActionInfoWithSchema> result)
        {
            var info = new ActionInfoWithSchema();
            if (cdo != null)
            {
                var cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(cdo);
                if (cachedInfo != null && cachedInfo.ActionTResultSchema != null)
                {
                    result.Add(cachedInfo);
                }
                else
                {
                    var cdoData = cdo as CompositeDataObject;
                    var schema = cdoData.BuildSchema();
                    schema.ActionID = cdoData.ID;
                    schema.ActionCode = cdoData.Code;
                    schema.ActionName = cdoData.Name;
                    schema.ResultType = DataType.List;
                    var cdoResultSchema = BuildEmptyTResultFromTSchema(cdoData, SPI.ActionType.CDO);
                    info.ActionID = id;
                    info.ActionCode = cdoData.Code;
                    info.ActionSchema = schema;
                    info.ActionTResultSchema = cdoResultSchema;
                    info.ActionType = SPI.ActionType.CDO;
                    info.LastModifyTime = cdo.LastModifyTime;
                    //info.ActionMetaData = cdoData;
                    info.ActionMetaData = new ActionMetaData()
                    {
                        ID = cdoData.MetaDataID,
                        Code = cdoData.MetaDataCode,
                        Name = cdoData.MetaDataName,
                    };
                    result.Add(info);
                    cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(cdo);
                    if (cachedInfo == null)
                    {
                        ActionRTPLocalCache.GetInstance().AddCache(cdo, info);
                    }
                }
            }
        }

        private List<ActionInfoWithSchema> GetInfoWithSchemaForEO(List<string> eoList)
        {
            var result = new List<ActionInfoWithSchema>();
            eoList.ForEach(id =>
            {
                var info = new ActionInfoWithSchema();
                var eo = MetadataRTPService.GetInstance().GetMetadata(id, SPI.ActionType.EO);
                if (eo != null)
                {
                    var cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(eo.MetaDataContent);
                    if (cachedInfo != null && cachedInfo.ActionTResultSchema != null)
                    {
                        result.Add(cachedInfo);
                    }
                    else
                    {
                        var eoData = eo.MetaDataContent as EntityObject;
                        var schema = eoData.BuildSchema();
                        schema.ActionID = eoData.ID;
                        schema.ActionCode = eoData.Code;
                        schema.ActionName = eoData.Name;
                        schema.ResultType = DataType.List;
                        var eoResultSchema = BuildEmptyTResultFromTSchema(eoData, SPI.ActionType.EO);
                        info.ActionID = id;
                        info.ActionSchema = schema;
                        info.ActionTResultSchema = eoResultSchema;
                        info.ActionType = SPI.ActionType.EO;
                        info.LastModifyTime = eoData.LastModifyTime;
                        //info.ActionMetaData = eoData;
                        info.ActionMetaData = new ActionMetaData()
                        {
                            ID = eoData.MetaDataID,
                            Code = eoData.MetaDataCode,
                            Name = eoData.MetaDataName,
                        };
                        result.Add(info);
                        cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(eo.MetaDataContent);
                        if (cachedInfo == null)
                        {
                            ActionRTPLocalCache.GetInstance().AddCache(eo.MetaDataContent, info);
                        }
                    }
                }
            });

            return result;
        }

        private List<ActionInfoWithSchema> GetInfoWithSchemaForDO(List<string> doList)
        {
            var result = new List<ActionInfoWithSchema>();
            doList.ForEach(id =>
            {
                var info = new ActionInfoWithSchema();
                var dataObject = MetadataRTPService.GetInstance().GetMetadata(id, SPI.ActionType.DO);
                if (dataObject != null)
                {
                    var cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(dataObject.MetaDataContent);
                    if (cachedInfo != null && cachedInfo.ActionTResultSchema != null)
                    {
                        result.Add(cachedInfo);
                    }
                    else
                    {
                        var doData = dataObject.MetaDataContent as DataObject;
                        var schema = doData.BuildSchema();
                        schema.ActionID = doData.ID;
                        schema.ActionCode = doData.Code;
                        schema.ActionName = doData.Name;
                        schema.ResultType = DataType.List;
                        var doResultSchema = BuildEmptyTResultFromTSchema(doData, SPI.ActionType.DO);
                        info.ActionID = id;
                        info.ActionSchema = schema;
                        info.ActionTResultSchema = doResultSchema;
                        info.ActionType = SPI.ActionType.DO;
                        info.LastModifyTime = doData.LastModifyTime;
                        //info.ActionMetaData = doData;
                        info.ActionMetaData = new ActionMetaData()
                        {
                            ID = doData.MetaDataID,
                            Code = doData.MetaDataCode,
                            Name = doData.MetaDataName,
                        };
                        result.Add(info);
                        cachedInfo = ActionRTPLocalCache.GetInstance().GetSchema(dataObject.MetaDataContent);
                        if (cachedInfo == null)
                        {
                            ActionRTPLocalCache.GetInstance().AddCache(dataObject.MetaDataContent, info);
                        }
                    }
                }
            });
            return result;
        }

        private TSchema GetActionTSchema(ActionMetaData action)
        {
            if (action == null || action.ReturnValueType == ReturnValueType.Void)
            {
                return new TSchema()
                {
                    ActionID = action?.ID,
                    ActionCode = action.Code,
                    ActionName = action.Name
                };
            }
            var schema = new TSchema();
            if (action.MethodParams != null && action.MethodParams.Count > 0)
            {
                var outParam = action.MethodParams.Find(x => x.ParamType == ParamType.Out);
                if (outParam != null)
                {
                    switch (outParam.ParamSourceType)
                    {
                        case ParamSourceType.DQ:
                            {
                                var dq = MetadataRTPService.GetInstance().GetMetadata(outParam.SourceID, SPI.ActionType.DQ).MetaDataContent as DataQuery;
                                schema = dq.BuildSchema();
                            }
                            break;
                        case ParamSourceType.CDO:
                            {
                                var cdo = MetadataRTPService.GetInstance().GetMetadata(outParam.SourceID, SPI.ActionType.CDO).MetaDataContent as CompositeDataObject;
                                schema = cdo.BuildSchema();
                            }
                            break;
                        case ParamSourceType.DO:
                        case ParamSourceType.SDO:
                            {
                                var doMetadata = MetadataRTPService.GetInstance().GetMetadata(outParam.SourceID, SPI.ActionType.DO).MetaDataContent as DataObject;
                                schema = doMetadata.BuildSchema();
                            }
                            break;
                        case ParamSourceType.Basic:
                            {
                                schema = BasicTypeData.BuildSchema(outParam.ParamValueType);
                            }
                            break;
                        case ParamSourceType.EO:
                        default:
                            {
                                var eo = MetadataRTPService.GetInstance().GetMetadata(outParam.SourceID, SPI.ActionType.EO).MetaDataContent as EntityObject;
                                schema = eo.BuildSchema();
                            }
                            break;
                    }
                }
            }
            return schema;
        }

        private ExprItem GetActionExprItem(ActionMetaData action)
        {
            var provider = new ActionDetailExpProvider();
            var exprItem = new ExprItem()
            {
                ID = action.ID,
                Code = action.Code,
                Name = action.Name,
                ExprMetaDataID = action.ID,
                IsLeaf = false,
                SortOrder = 0,
            };
            provider.GetExprChildrenItems(exprItem, true, "");
            return exprItem;
        }

        private TRecord BuildEmptyTResultFromTSchema(IMetaDataContent metaData, SPI.ActionType actionType)
        {
            var exprItem = new ExprItem();
            switch (actionType)
            {
                case SPI.ActionType.Action:
                    {
                        var provider = new ActionDetailExpProvider();
                        var action = ActionRTPUtil.ActionMetadataValid(metaData);
                        exprItem = new ExprItem()
                        {
                            ID = action.ID,
                            Code = action.Code,
                            Name = action.Name,
                            ExprMetaDataID = action.ID,
                            IsLeaf = false,
                            SortOrder = 0,
                            ItemType = ItemType.Action
                        };
                        provider.GetMetadata += Provider_GetMetadata;
                        return provider.BuildTResultSchema(exprItem);
                    }
                case SPI.ActionType.DQ:
                    {
                        var provider = new DataQueryDetailExpProvider();
                        provider.GetMetadata += Provider_GetMetadata;
                        var dataQuery = metaData as DataQuery;
                        exprItem = new ExprItem()
                        {
                            ID = dataQuery.ID,
                            Code = dataQuery.Code,
                            Name = dataQuery.Name,
                            ExprMetaDataID = dataQuery.ID,
                            IsLeaf = false,
                            SortOrder = 0,
                            ItemType = ItemType.DataQuery
                        };
                        return provider.BuildTResultSchema(exprItem);
                    }
                case SPI.ActionType.CDO:
                    {
                        var cdo = metaData as CompositeDataObject;
                        exprItem = new ExprItem()
                        {
                            ID = cdo.ID,
                            Code = cdo.Code,
                            Name = cdo.Name,
                            ExprMetaDataID = cdo.ID,
                            IsLeaf = false,
                            SortOrder = 0,
                            ItemType = ItemType.CDO
                        };
                        var provider = CommonExpProvider.GetInstance();
                        provider.GetMetadata += Provider_GetMetadata;
                        var recordResult = provider.BuildTResultSchema(exprItem);
                        return recordResult;
                        //var tResult = StateMachineRTPClient.GetInstance().InitRecord(cdo.ID, recordResult);
                        //return (tResult as TCommonResult<TRecord>).Record;
                        //return tResult.GetDataContent() as TRecord;
                    }
                case SPI.ActionType.DO:
                    {
                        var dataObject = metaData as DataObject;
                        exprItem = new ExprItem()
                        {
                            ID = dataObject.ID,
                            Code = dataObject.Code,
                            Name = dataObject.Name,
                            ExprMetaDataID = dataObject.ID,
                            IsLeaf = false,
                            SortOrder = 0,
                            ItemType = ItemType.DO
                        };
                        var provider = CommonExpProvider.GetInstance();
                        provider.GetMetadata += Provider_GetMetadata;
                        return provider.BuildTResultSchema(exprItem);
                    }
                case SPI.ActionType.EO:
                    {
                        var entityObject = metaData as EntityObject;
                        exprItem = new ExprItem()
                        {
                            ID = entityObject.ID,
                            Code = entityObject.Code,
                            Name = entityObject.Name,
                            ExprMetaDataID = entityObject.ID,
                            IsLeaf = false,
                            SortOrder = 0,
                            ItemType = ItemType.EO
                        };
                        var provider = CommonExpProvider.GetInstance();
                        provider.GetMetadata += Provider_GetMetadata;
                        return provider.BuildTResultSchema(exprItem);
                    }
                default:
                    return null;
            }
        }

        private IMetaDataContent Provider_GetMetadata(string metaDataID)
        {
            return MetaDataClientService.GetInstance().GetMetaDataContent(metaDataID);
        }
    }
}
